package job.gfp.ranks;

import java.io.IOException;
import java.util.Set;

import job.gfp.ranks.base.Data;
import job.gfp.ranks.base.ValueMerger;
import job.gfp.ranks.base.ValueType;

import org.apache.hadoop.mapred.Reporter;

import util.UrlUtil;

public class Customize {

    public static final double DELTA = 1e-4;
    
    private static final double LAMBDA = 0.15;

    public static enum DataType {
        OUT_DEGREE(ValueType.LONG, ValueMerger.MAX), PAGE_RANK(
                ValueType.DOUBLE, ValueMerger.ADD), TRUST_RANK(
                ValueType.DOUBLE, ValueMerger.ADD), BIAS(ValueType.DOUBLE,
                ValueMerger.MAX);

        ValueType type;

        ValueMerger merger;

        DataType(ValueType type, ValueMerger merger) {
            this.type = type;
            this.merger = merger;
        }

        public String mergeValue(String prev, String cur) {
            return type.merge(merger, prev, cur);
        }

        @SuppressWarnings("unchecked")
        public <T> T parse(String s) {
            return (T) type.parse(s);
        }

    }

    public static Data initialData(String url, long outDegree,
            Set<String> seeds, double times) {
        Data ret = new Data();
        
        ret.set(DataType.PAGE_RANK, 1);
        
        ret.set(DataType.TRUST_RANK, 1);
        if (seeds.contains(UrlUtil.getHost(url))) {
            ret.set(DataType.BIAS, times);
        } else {
            ret.set(DataType.BIAS, 0);
        }
        
        ret.set(DataType.OUT_DEGREE, outDegree);
        
        return ret;
    }

    public static String readable(Data rank, long websize) {
        Double pagerank = rank.getValue(DataType.PAGE_RANK);
        Double trustrank = rank.getValue(DataType.TRUST_RANK);

        return (pagerank / websize) + "\t" + (trustrank / websize);
    }

    public static Data send(String source, Data rank, String target, long weight)
            throws IOException {
        Data ret = new Data();
        Long outDegree = rank.getValue(DataType.OUT_DEGREE);

        Double pagerank = rank.getValue(DataType.PAGE_RANK);
        ret.set(DataType.PAGE_RANK, pagerank * weight / outDegree);

        Double trustrank = rank.getValue(DataType.TRUST_RANK);
        ret.set(DataType.TRUST_RANK, trustrank * weight / outDegree);
        ret.set(DataType.BIAS, 0);

        ret.set(DataType.OUT_DEGREE, 0);

        return ret;
    }

    public static void delta(Data cur, Data old, Reporter reporter) {
        delta(cur, old, DataType.PAGE_RANK, reporter);
        delta(cur, old, DataType.TRUST_RANK, reporter);
    }

    public static Data fomular(Data data) {
        Data ret = new Data();

        Double pagerank = data.getValue(DataType.PAGE_RANK);
        ret.set(DataType.PAGE_RANK, pagerank * (1 - LAMBDA) + LAMBDA);

        Double trustrank = data.getValue(DataType.TRUST_RANK);
        Double bias = data.getValue(DataType.BIAS);
        ret.set(DataType.TRUST_RANK, trustrank * (1 - LAMBDA) + LAMBDA * bias);

        ret.set(DataType.BIAS, data.get(DataType.BIAS));

        ret.set(DataType.OUT_DEGREE, data.get(DataType.OUT_DEGREE));

        return ret;
    }

    public static Data fomularZ(Data z, long websize) {
        Data ret = new Data();

        Double pagerank = z.getValue(DataType.PAGE_RANK);
        ret.set(DataType.PAGE_RANK, pagerank * 0.85 / websize);

        Double trustrank = z.getValue(DataType.TRUST_RANK);
        ret.set(DataType.TRUST_RANK, trustrank * 0.85 / websize);

        ret.set(DataType.OUT_DEGREE, 0);
        return ret;
    }

    private static void delta(Data cur, Data old, DataType type,
            Reporter reporter) {
        Double c = cur.getValue(type);
        Double o = old.getValue(type);
        double delta = Math.abs(c - o);
        if (delta >= Customize.DELTA) {
            reporter.incrCounter("Delta", type.toString(), 1);
            long d = (long) Math.round(delta / Customize.DELTA);
            reporter.incrCounter("Distance", type.toString(), d);
        }
    }

}
